//#stdafx.h

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>

#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")

#define TCP_PORT    10066
#define MAX_BUFFER_LENGTH	1024

typedef struct tag_PER_IO_OPERATION_DATA 
{
	WSAOVERLAPPED wsaOverLapped;
	WSABUF wsaBuf;
	char szMessage[MAX_BUFFER_LENGTH];
	DWORD dwNumOfBytesRecved;
	DWORD dwFlag;

	tag_PER_IO_OPERATION_DATA()
	{
		memset(&wsaOverLapped, 0, sizeof(WSAOVERLAPPED));
		memset(&wsaBuf, 0, sizeof(WSABUF));
		memset(szMessage, 0, sizeof(szMessage));
		dwNumOfBytesRecved = 0;
		dwFlag = 0;
	}
	~tag_PER_IO_OPERATION_DATA()
	{
		memset(&wsaOverLapped, 0, sizeof(WSAOVERLAPPED));
		memset(&wsaBuf, 0, sizeof(WSABUF));
		memset(szMessage, 0, sizeof(szMessage));
		dwNumOfBytesRecved = 0;
		dwFlag = 0;
	}
}PER_IO_OPERATION_DATA, *pPER_IO_OPERATION_DATE;

//IOCP.cpp

#include "stdafx.h"

DWORD WINAPI WorkerThread(LPVOID CompletionPortId)
{
    HANDLE hCompletionPort = (HANDLE)CompletionPortId;
	DWORD dwBYteTransferred;
	SOCKET soClient;
	pPER_IO_OPERATION_DATE pPerData = NULL;
	
	while (true)
	{
		::GetQueuedCompletionStatus(hCompletionPort, &dwBYteTransferred, (PULONG_PTR)&soClient, (LPOVERLAPPED*)&pPerData, INFINITE);
		if (dwBYteTransferred == 0xFFFFFFFF)
		{
			return 0;
		}
		if (dwBYteTransferred == 0)
		{
			::closesocket(soClient);
			::HeapFree(::GetProcessHeap(), HEAP_ZERO_MEMORY, pPerData);
		}
		else
		{
			pPerData->szMessage[dwBYteTransferred] = '\0';
			pPerData->wsaBuf.len = MAX_BUFFER_LENGTH;
			pPerData->wsaBuf.buf = pPerData->szMessage;

			printf("Receive Message:(%d) %s\n", pPerData->wsaBuf.len, pPerData->wsaBuf.buf);

			::WSARecv(soClient, &pPerData->wsaBuf, 1, &pPerData->dwNumOfBytesRecved, &pPerData->dwFlag, &pPerData->wsaOverLapped, NULL);
		}
	}

	return 0;
}

int _tmain(int argc, _TCHAR* argv[])
{
	WORD dwVersion = MAKEWORD(2, 2);
	WSAData wsaData;
	if (WSAStartup(dwVersion, &wsaData) != 0)
	{
		return -1;
	}
	HANDLE hCompletionPort = NULL;
	SOCKET soSock;
	
	do
	{
		hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
		if (NULL == hCompletionPort)
		{
			break;
		}

		SYSTEM_INFO sysInfo;
		DWORD dwThreadId;
		::GetSystemInfo(&sysInfo);
		for (int i = 0; i < sysInfo.dwNumberOfProcessors; i++)
		{
			::CreateThread(NULL, 0, WorkerThread, hCompletionPort, 0, &dwThreadId);
		}

		soSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
		if (SOCKET_ERROR == soSock)
		{
			break;
		}

		SOCKADDR_IN addrSvr;
		addrSvr.sin_family = AF_INET;
		addrSvr.sin_addr.s_addr = htonl(INADDR_ANY);
		addrSvr.sin_port = htons(TCP_PORT);
		if (SOCKET_ERROR == bind(soSock, (sockaddr*)(&addrSvr), sizeof(sockaddr)))
		{
			break;
		}

		if (SOCKET_ERROR == listen(soSock, 5))
		{
			break;
		}

		int nAddrSize = sizeof(SOCKADDR_IN);
		PER_IO_OPERATION_DATA* pPerData = NULL;

		while (true)
		{
			SOCKADDR_IN addrClient;
			SOCKET soClient = accept(soSock, (sockaddr*)(&addrClient), &nAddrSize);

			if (INVALID_SOCKET != soClient)
			{
				printf("Accepted client: %s: %d\n", inet_ntoa(addrClient.sin_addr), ntohs(addrClient.sin_port));

				CreateIoCompletionPort((HANDLE)soClient, hCompletionPort, (DWORD)soClient, 0);

				pPerData = (pPER_IO_OPERATION_DATE)::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PER_IO_OPERATION_DATA));
				if (pPerData)
				{
					pPerData->wsaBuf.len = MAX_BUFFER_LENGTH;
					pPerData->wsaBuf.buf = pPerData->szMessage;

					::WSARecv(soClient, &pPerData->wsaBuf, 1, &pPerData->dwNumOfBytesRecved, &pPerData->dwFlag, &pPerData->wsaOverLapped, NULL);
				}
				else
				{
					::closesocket(soClient);
					soClient = INVALID_SOCKET;
				}
			}
		}

	} while (0);
	
	if (hCompletionPort != NULL)
	{
		::PostQueuedCompletionStatus(hCompletionPort, 0xFFFFFFFF, 0, NULL);

		::CloseHandle(hCompletionPort);
		hCompletionPort = NULL;
	}
	if (soSock != INVALID_SOCKET)
	{
		::closesocket(soSock);
		soSock = INVALID_SOCKET;
	}
	WSACleanup();

	return 0;
}